/* FFT implementation FOR PURE DATA: based on tab_fft from iem_tab lib.
 *  
 *  by Adriano Monteiro, february, 17, 2013. contact: monteiro.adc@gmaill.com
 *  
 */

#include "m_pd.h"
#include <stdlib.h>
#include <math.h>
#include "FFT_simple.h"



static t_class *simple_fft_class;

typedef struct _simple_fft {
        t_object x_obj;
        
  
        // leitura da array source
        t_word *src_vec;
        t_symbol *src_arrayname;
    	int src_array_points;
    	
        // leitura da array destine
        t_word *dst_re_vec;
        t_symbol *dst_re_arrayname;
       	int dst_re_array_points;
       	
       	t_word *dst_im_vec;
       	t_symbol *dst_im_arrayname;
       	int dst_im_array_points;
                
        
        // outlets
                   
        
}t_simple_fft;

/*********************** INTERNAL FUNCTIONS  *******************************/




/******************* BANG MESSAGE ***************************************/

void simple_fft_bang(t_simple_fft *x) {
	
	int i;
	float zeroPad = 0; 
	complex *tDm, *fDm;
	t_garray *a, *b, *c;


	// access array
	if(!(a = (t_garray *)pd_findbyclass(x->src_arrayname, garray_class)))
	  	pd_error(x, "%s: no such array", x->src_arrayname->s_name);
	else if(!garray_getfloatwords(a, &x->src_array_points, &x->src_vec))
		pd_error(x, "%s: bad template for wav2aud", x->src_arrayname->s_name);	
	
	if(!(b = (t_garray *)pd_findbyclass(x->dst_re_arrayname, garray_class)))
		pd_error(x, "%s: no such array", x->dst_re_arrayname->s_name);
	else if(!garray_getfloatwords(b, &x->dst_re_array_points, &x->dst_re_vec))
		pd_error(x, "%s: bad template for wav2aud", x->dst_re_arrayname->s_name);		
	
	if(!(c = (t_garray *)pd_findbyclass(x->dst_im_arrayname, garray_class)))
		pd_error(x, "%s: no such array", x->dst_im_arrayname->s_name);
	else if(!garray_getfloatwords(c, &x->dst_im_array_points, &x->dst_im_vec))
		pd_error(x, "%s: bad template for wav2aud", x->dst_im_arrayname->s_name);	
	  		 
	
	if(fmod(log2(x->src_array_points),1))
        zeroPad = pow(2, (int)(log2(x->src_array_points)+1));     
    else
        zeroPad = x->src_array_points;
        
    if(zeroPad > x->dst_im_array_points || zeroPad > x->dst_re_array_points){
        pd_error(x, "simple_fft zero-pads non 2^n fft-sizes. Your %d-points source-array requires destination arrays with a minimum of %d points", (int)x->src_array_points,(int)zeroPad);
    }else{
            
	
    	tDm = (complex *)getbytes(zeroPad * sizeof(complex));
    	
    	for(i=0; i < x->src_array_points; i++){ 
    		tDm[i].re = (double) x->src_vec[i].w_float; // copy array
    		tDm[i].im = 0;
    	}
    
        for(i = x->src_array_points; i < zeroPad; i++){
         	tDm[i].re = 0; // zero Padding
        	tDm[i].im = 0;
        }
        
        fDm = FFT_simple(tDm, zeroPad);
        
        for(i=0; i < zeroPad; i++)x->dst_re_vec[i].w_float = (float) fDm[i].re;
    	for(i=0; i < zeroPad; i++)x->dst_im_vec[i].w_float = (float) fDm[i].im;
    		
    	garray_redraw(b);
    	garray_redraw(c);
    	
    	// free local memory
    	freebytes(fDm, zeroPad * sizeof(complex));
    	freebytes(tDm, zeroPad * sizeof(complex));
    }
		
    
}

/****************************** DATA ACESS METHOD **********************************/


		
/************************** OBJECT CREATION AND SETUP *************************************/


void *simple_fft_new(t_symbol *s, int argc, t_atom *argv){
     t_simple_fft *x = (t_simple_fft *)pd_new(simple_fft_class);
     
  // acesso a array copiado do tabletool do brent 	
	t_garray *a, *b, *c;
 		
  	if(atom_getsymbol(argv)){
  		x->src_arrayname = atom_getsymbol(argv);

  		if(!(a = (t_garray *)pd_findbyclass(x->src_arrayname, garray_class)))
  		        pd_error(x, "%s: no such array", x->src_arrayname->s_name);
  		 else if(!garray_getfloatwords(a, &x->src_array_points, &x->src_vec))
  		    	pd_error(x, "%s: bad template for wav2aud", x->src_arrayname->s_name);	
  		}
  		else
  			post("WARNING: no array specified.");
  		
  	if(atom_getsymbol(argv+1)){
  		 x->dst_re_arrayname = atom_getsymbol(argv+1);

  		 if(!(b = (t_garray *)pd_findbyclass(x->dst_re_arrayname, garray_class)))
  		        pd_error(x, "%s: no such array", x->dst_re_arrayname->s_name);
  		 else if(!garray_getfloatwords(b, &x->dst_re_array_points, &x->dst_re_vec))
  		     	pd_error(x, "%s: bad template for wav2aud", x->dst_re_arrayname->s_name);	
  		 }
  		 else
  		 		post("WARNING: no array specified.");
  	
	if(atom_getsymbol(argv+2)){
  		 x->dst_im_arrayname = atom_getsymbol(argv+2);

  		 if(!(c = (t_garray *)pd_findbyclass(x->dst_im_arrayname, garray_class)))
  		        pd_error(x, "%s: no such array", x->dst_im_arrayname->s_name);
  		 else if(!garray_getfloatwords(c, &x->dst_im_array_points, &x->dst_im_vec))
  		     	pd_error(x, "%s: bad template for wav2aud", x->dst_im_arrayname->s_name);	
  		 }
  		 else
  		 		post("WARNING: no array specified.");
  	
	
  	// allocate memory for frame output as list
 	//x->frame_out = (t_atom *)getbytes(M * sizeof(t_atom));	
  	
  		
    // cria os outlets
  	//x->x_list = outlet_new(&x->x_obj, gensym("list")); 
     
     return (void *) x;
}

    
void simple_fft_setup(void) {
	 simple_fft_class = class_new(gensym("simple_fft"), (t_newmethod)simple_fft_new, 
     0, sizeof(t_simple_fft), CLASS_DEFAULT, A_GIMME, 0);
      
    class_addbang(simple_fft_class, simple_fft_bang);
 
}

